﻿using Microsoft.AspNet.Hosting;
using Microsoft.Data.Entity;
using Microsoft.Extensions.Caching.Distributed;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.PlatformAbstractions;
using Newtonsoft.Json;
using Sino.CapacityCloud.Infrastructure.Events;
using Sino.CapacityCloud.Infrastructure.Services;
using Sino.CapacityCloud.Repositories.Models;
using Sino.CapacityCloud.WebApi;
using Sino.CommonServices.EventTypes;
using Sino.CommonServices.MessageTypes;
using Sino.Tms.Primitives;
using Sino.TMSystem;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Threading.Tasks;
using System.Reflection;
using ChineseAreaService;
using System.Globalization;
using Microsoft.AspNet.Identity;
using System.Security.Claims;
using Sino.CapacityCloud.Repositories.EventData;
using AutoMapper;

namespace Sino.CapacityCloud.Services
{

    /// <summary>
    /// Sync data from TMS server to this App server.
    /// </summary>
    public class EventHandlers
        : BackgroundWorker, IEventHandlers
    {

        public const string ConfigurationFileName = "appsettings";

        private static int InstanceCount = 0;

        private readonly IDistributedCache _cache;
        private readonly ITmsServiceBus _tmsServiceBus;
        private readonly IServiceProvider _serviceProvider;
        private readonly IBankCardService _bankCard;

        private readonly IEventBus _eventBus;
        private readonly IServiceBusRepeater _serviceBusRepeater;

        private readonly ILoggerFactory _loggerFactory;
        private readonly ISerializer _serializer;
        private readonly IChinaAreaService _areaService;

        public Microsoft.Extensions.Configuration.IConfiguration Configuration { get; }

        public IServiceProvider ServiceProvider
        {
            get
            {
                var scope = this._serviceProvider.GetRequiredService<IServiceScopeFactory>().CreateScope();
                return scope.ServiceProvider;
            }
        }

        public ApplicationUserManager UserManager
        {
            get
            {
                return this.ServiceProvider.GetService<ApplicationUserManager>();
            }
        }

        public ApplicationRoleManager RoleManager
        {
            get
            {
                return this.ServiceProvider.GetService<ApplicationRoleManager>();
            }
        }

        public CarManager CarManager
        {
            get
            {
                return this.ServiceProvider.GetService<CarManager>();
            }
        }

        public TransportLinkageManager TransportLinkageManager
        {
            get
            {
                return this.ServiceProvider.GetService<TransportLinkageManager>();
            }
        }

        public string DomainName { get; }

        public EventHandlers(
            ILoggerFactory loggerFactory,
            IApplicationEnvironment appEnv,
            IHostingEnvironment env,
            IDistributedCache cache,
            IEventBus eventBus,
            IRandomPasswordService randomPasswordService,
            ISerializer serialier,
            IChinaAreaService areaService,
            ITmsServiceBus tmsServiceBus,
            IBankCardService bankCard,
            IServiceProvider serviceProvider,
            IServiceBusRepeater serviceBusRepeater,
            CarManager carManager,
            ApplicationUserManager userManager,
            ApplicationRoleManager roleManager)
            : base(loggerFactory)
        {
            this._cache = cache;
            this._areaService = areaService;
            this._tmsServiceBus = tmsServiceBus;
            this._bankCard = bankCard;
            this._serviceProvider = serviceProvider;

            this._loggerFactory = loggerFactory;
            this._serializer = serialier;
            this._serviceBusRepeater = serviceBusRepeater;

            this._eventBus = eventBus;

            //this.UserManager = userManager;
            //this.RoleManager = roleManager;
            //this.CarManager = carManager;

            var configurationBuilder = new ConfigurationBuilder()
                 .SetBasePath(appEnv.ApplicationBasePath)
                .AddJsonFile($"{ConfigurationFileName}.json")
                .AddJsonFile($"{ConfigurationFileName}.{env.EnvironmentName}.json", optional: true)
                .AddEnvironmentVariables();

            this.Configuration = configurationBuilder.Build();
            this.DomainName = Configuration.GetSection("AppSettings")?["DomainName"] ?? "@site.com";

            this._logger.LogInformation($"Starting TmsSyncBackgroundWorker {InstanceCount++}");

            this.Start();
        }

        public override void Start()
        {
            if (!this._eventBus.IsRunning)
            {
                this._eventBus.Start();
            }
            this._eventBus.Register(this);
        }

        public override void Stop(int timeout = 2000)
        {
            this._eventBus?.Unregister(this);
        }

        protected async override Task<bool> Process()
        {
            return await Task.FromResult(false);
        }

        [EventSubscriber]
        public void HandleEvent(CarAddApplicationEventData carAddCommitionEventData)
        {
            Task.Run(async () =>
            {
                try
                {
                    this._logger.LogInformation($"Got {nameof(CarAddApplicationEventData)} event: {0}, CarCode: {carAddCommitionEventData?.CarCode}: \r\n{carAddCommitionEventData?.ToJson()}");
                    if (carAddCommitionEventData != null && !string.IsNullOrWhiteSpace(carAddCommitionEventData.CarCode))
                    {
                        using (var userManager = this.UserManager)
                        {
                            var user = await userManager.CreateUserAsync(new UserDetails()
                            {
                                PhoneNumber = carAddCommitionEventData.PhoneNumber,
                                Code = carAddCommitionEventData.CarCode,
                                RealName = carAddCommitionEventData.Driver,
                                CarriageType = carAddCommitionEventData.CarriageType,
                            });
                            var details = Mapper.Map(carAddCommitionEventData, new CarDetails());
                            details.Users = new List<UserBasicInfo>() { Mapper.Map(user, new UserBasicInfo()) };
                            var carId = this.CarManager.AddOrUpdateCarInfo(carAddCommitionEventData.CarCode,
                                carAddCommitionEventData.Length,
                                carAddCommitionEventData.CarriageType,
                                carAddCommitionEventData.Type,
                                details,
                                carAddCommitionEventData.CarAddingModerationCommitId,
                                carAddCommitionEventData.AssignmentOfLoad,
                                carAddCommitionEventData.Source);
                            if (string.IsNullOrWhiteSpace(user.CarId))
                            {
                                user.CarId = carId;
                            }
                            if (user.CarId == carId)
                            {
                                if (string.IsNullOrWhiteSpace(user.Address))
                                {
                                    user.Address = carAddCommitionEventData.Address;
                                }
                                if (string.IsNullOrWhiteSpace(user.RealName))
                                {
                                    user.RealName = carAddCommitionEventData.Driver;
                                }
                                if (user.CarriageType == CarriageType.None)
                                {
                                    user.CarriageType = carAddCommitionEventData.CarriageType;
                                }
                            }
                            await userManager.UpdateAsync(user);
                        }
                    }
                }
                catch (Exception ex)
                {
                    this._logger.LogError(ex.Message + Environment.NewLine + ex.StackTrace, ex);
                }
            });
        }

        [EventSubscriber]
        public void HandleEvent(CarDetails carDetailsEventData)
        {
            Task.Run(async () =>
            {
                try
                {
                    this._logger.LogInformation($"Got {nameof(CarDetails)} event: {0}, CarCode: {carDetailsEventData?.Code}: \r\n{carDetailsEventData?.ToJson()}");
                    if (carDetailsEventData != null && !string.IsNullOrWhiteSpace(carDetailsEventData.Code))
                    {
                        var carId = this.CarManager.AddOrUpdateCarInfo(carDetailsEventData.Code,
                            carDetailsEventData.Length, CarriageType.None,
                            carDetailsEventData.Type, carDetailsEventData);

                        foreach (var userInfo in carDetailsEventData.Users)
                        {
                            using (var userManager = this.UserManager)
                            {
                                var user = await userManager.CreateUserAsync(new UserDetails()
                                {
                                    PhoneNumber = userInfo.PhoneNumber,
                                    RealName = userInfo.RealName,
                                    CarriageType = userInfo.CarriageType
                                });
                                if (user != null)
                                {
                                    if (string.IsNullOrWhiteSpace(user.CarId))
                                    {
                                        user.CarId = carId;
                                        await userManager.UpdateAsync(user);
                                    }
                                    if (string.IsNullOrWhiteSpace(user.DrivingLicenseKey))
                                    {
                                        user.DrivingLicenseKey = userInfo.DrivingLicenseImageKey;
                                        user.DrivingLicenseKeyCertificationState = userInfo.DrivingLicenseImageKey != null ? CertificationStatus.Passed : CertificationStatus.Uncommitted;
                                        await userManager.UpdateAsync(user);
                                    }
                                    if (string.IsNullOrWhiteSpace(user.IdentityCardKey))
                                    {
                                        user.IdentityCardKey = userInfo.IdentityCardImageKey;
                                        user.IdentityCardCertificationState = userInfo.IdentityCardImageKey != null ? CertificationStatus.Passed : CertificationStatus.Uncommitted;
                                        await userManager.UpdateAsync(user);
                                    }
                                    if (user.CarriageType == CarriageType.None)
                                    {
                                        user.CarriageType = userInfo.CarriageType;
                                        await userManager.UpdateAsync(user);
                                    }
                                    if (string.IsNullOrWhiteSpace(user.Address))
                                    {
                                        user.Address = userInfo.Address;
                                        await userManager.UpdateAsync(user);
                                    }
                                    foreach (var card in userInfo.BankCards)
                                    {
                                        try
                                        {
                                            await Task.Delay(1000);
                                            await this._bankCard.AddBankCardAsync(user, card.Holder, card.Number);
                                        }
                                        catch
                                        {
                                        }
                                    }
                                    foreach (var line in userInfo.CommonLines)
                                    {
                                        try
                                        {
                                            this.TransportLinkageManager.AddOrUpdateTransportLinkage(
                                                user, Mapper.Map(line, new TransportLinkage()));
                                        }
                                        catch
                                        {
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
                catch (Exception ex)
                {
                    this._logger.LogError(ex.Message + Environment.NewLine + ex.StackTrace, ex);
                }
            });
        }

        [EventSubscriber]
        public void HandleEvent(UserDetails userAddEventData)
        {
            Task.Run(async () =>
            {
                try
                {
                    this._logger.LogInformation($"Got {nameof(UserDetails)} event: {0}, PhoneNumber: {userAddEventData?.PhoneNumber}: \r\n{userAddEventData?.ToJson()}");
                    if (userAddEventData != null && !string.IsNullOrWhiteSpace(userAddEventData.PhoneNumber))
                    {
                        using (var userManager = this.UserManager)
                        {
                            var user = await this.UserManager.CreateUserAsync(userAddEventData);
                            if (user != null)
                            {
                                this._logger.LogInformation($"Successfully updated a new user: {user.Id}.");
                                if (!string.IsNullOrEmpty(userAddEventData.Code))
                                {
                                    var carId = this.CarManager.AddOrUpdateCarInfo(userAddEventData.Code);
                                    user.CarId = carId;
                                    await userManager.UpdateAsync(user);
                                }
                            }
                            else
                            {
                                this._logger.LogError($"Failed to update a new user: {userAddEventData.PhoneNumber}.");
                            }
                        }
                    }
                }
                catch (Exception ex)
                {
                    this._logger.LogError(ex.Message + Environment.NewLine + ex.StackTrace, ex);
                }
            });
        }

        [EventSubscriber]
        public void HandleEvent(StartTransportEventData startTransportEventData)
        {
            try
            {
                this._logger.LogError($"Got {nameof(StartTransportEventData)} event: \r\n{startTransportEventData?.ToJson()}");
                if (startTransportEventData.IsReturn)
                {
                    return;
                }
                var orderIds = startTransportEventData.OrderIds;
                if (orderIds == null || orderIds.Count() == 0)
                {
                    this._logger.LogError($"The given StartTransportEventData has no valid orderId."
                        + Environment.NewLine + JsonConvert.SerializeObject(startTransportEventData));
                    return;
                }
                Task.Run(async () => await this.ProcessOrderStartMessageAsync(orderIds));
            }
            catch (Exception ex)
            {
                this._logger.LogError(ex.Message + Environment.NewLine + ex.StackTrace, ex);
            }
        }

        [EventSubscriber]
        public void HandleEvent(EvaluateAddEventData evaluateAddEventData)
        {
            try
            {
                this._logger.LogInformation($"Got {nameof(EvaluateAddEventData)} event: {evaluateAddEventData?.ToJson()}");
            }
            catch (Exception ex)
            {
                this._logger.LogError(ex.Message + Environment.NewLine + ex.StackTrace, ex);
            }
        }

        [EventSubscriber]
        public void HandleEvent(OrderAuditedEventData orderAuditedEventData)
        {
            this._logger.LogInformation($"Got {nameof(OrderAuditedEventData)} event: \r\n{orderAuditedEventData?.ToJson()}");
        }

        [EventSubscriber]
        public void HandleEvent(GrabbedOrderEventData grabbedOrderEventData)
        {
            this._logger.LogError($"Got {nameof(GrabbedOrderEventData)} event: \r\n{grabbedOrderEventData?.ToJson()}");
            this._serviceBusRepeater.PublishAsync(typeof(GrabbedOrderEventData).FullName, grabbedOrderEventData);
        }

        [EventSubscriber]
        public void HandleEvent(CarAddEventData carAddEventData)
        {
            this._logger.LogError($"Got {nameof(CarAddEventData)} event: \r\n{carAddEventData?.ToJson()}");
            this._serviceBusRepeater.PublishAsync(typeof(CarAddEventData).FullName, carAddEventData);
        }

        [EventSubscriber]
        public void HandleEvent(PaymentEventData paymentEventData)
        {
            this._logger.LogError($"Got {nameof(PaymentEventData)} event: \r\n{paymentEventData?.ToJson()}");
            this._serviceBusRepeater.PublishAsync(typeof(PaymentEventData).FullName, paymentEventData);
        }

        [EventSubscriber]
        public void HandleEvent(TMSystem.PaymentResultEventData paymentApplicationResultEventData)
        {
            try
            {
                this._logger.LogError($"Got {typeof(TMSystem.PaymentResultEventData).FullName} event: \r\n{paymentApplicationResultEventData?.ToJson()}");
                Task.Run(async () => await ProcessPaymentApplicationResultEventDataAsync(paymentApplicationResultEventData));
            }
            catch (Exception ex)
            {
                this._logger.LogError(ex.Message + Environment.NewLine + ex.StackTrace, ex);
            }
        }

        [EventSubscriber]
        public void HandleEvent(PaymentsSystem.Event.PaymentResultEventData paymentResultEventData)
        {
            try
            {
                this._logger.LogError($"Got {typeof(PaymentsSystem.Event.PaymentResultEventData).FullName} event: \r\n{paymentResultEventData?.ToJson()}");
                Task.Run(async () => await ProcessPaymentResultEventDataAsync(paymentResultEventData));
            }
            catch (Exception ex)
            {
                this._logger.LogError(ex.Message + Environment.NewLine + ex.StackTrace, ex);
            }
        }

        private async Task ProcessPaymentApplicationResultEventDataAsync(TMSystem.PaymentResultEventData paymentResultEventData)
        {
            try
            {
                if (paymentResultEventData.Status == TMSystem.PaymentResultStatus.Paid)
                {
                    this._logger.LogError($"PaymentApplicationResultEventData: The fee {paymentResultEventData.FeeSettleBillId} has been paid.");
                    return;
                }
                if (paymentResultEventData.Status == TMSystem.PaymentResultStatus.InPayment)
                {
                    this._logger.LogError($"PaymentApplicationResultEventData: The fee {paymentResultEventData.FeeSettleBillId} has been in payment process.");
                    return;
                }
                using (var serviceScope = this._serviceProvider.GetRequiredService<IServiceScopeFactory>().CreateScope())
                {
                    using (var dbContext = serviceScope.ServiceProvider.GetService<ApplicationDbContext>())
                    {
                        var findEntity = dbContext.PaymentHistories.FirstOrDefault(x => Guid.Parse(x.SettleBillId) == paymentResultEventData.FeeSettleBillId);
                        if (findEntity != null)
                        {
                            findEntity.PaymentResultStatus = (Tms.Primitives.PaymentResultStatus)paymentResultEventData.Status;
                            var carrierOrder = await this._tmsServiceBus.GetMyOrderDetailsAsync(findEntity.CarrierOrderId);
                            if (carrierOrder != null)
                            {
                                var deliveryTime = string.Format(CultureInfo.GetCultureInfo("zh-CN"), "{0:U}", carrierOrder.DeliveryTime);
                                var message = $"{deliveryTime}" +
                                    $"从{carrierOrder.TransportLinkage.StartingPlaceName}到{carrierOrder.TransportLinkage.DestinationPlaceName}" +
                                    $"（{carrierOrder.OrderNumber}）{(carrierOrder.Type == OrderType.Bulk ? "" : "[零担]")}" +
                                    $"{findEntity.Type.GetCustomAttribute<EnumTextMetaAttribute>().Text}" +
                                    $"{findEntity.Price}元您申请提现失败，" +
                                    $"失败原因：{paymentResultEventData?.ErrorInfo ?? "未知"}。";
                                var orderModel = this.ToAppOrderModel(carrierOrder, findEntity.UserId, dbContext);
                                await this.SendRongCloudMessage("System", findEntity.UserId, message, orderModel.ToJson());
                            }
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                this._logger.LogError(ex.Message + Environment.NewLine + ex.StackTrace, ex);
            }
        }

        private async Task ProcessPaymentResultEventDataAsync(PaymentsSystem.Event.PaymentResultEventData paymentResultEventData)
        {
            try
            {
                if (paymentResultEventData.Status == PaymentsSystem.Dto.PaymentStatusOutPut.PaySuccess)
                {
                    this._logger.LogError($"PaymentResultEventData: The fee {paymentResultEventData.PayCode} has been paid.");
                }
                else
                {
                    this._logger.LogError($"PaymentResultEventData: The fee {paymentResultEventData.PayCode} has been in payment process.");
                }
                using (var serviceScope = this._serviceProvider.GetRequiredService<IServiceScopeFactory>().CreateScope())
                {
                    using (var dbContext = serviceScope.ServiceProvider.GetService<ApplicationDbContext>())
                    {
                        var findEntity = dbContext.PaymentHistories.FirstOrDefault(x => x.SettleBillCode == paymentResultEventData.PayCode);
                        if (findEntity != null)
                        {
                            var carrierOrder = await this._tmsServiceBus.GetMyOrderDetailsAsync(findEntity.CarrierOrderId);
                            if (carrierOrder != null)
                            {
                                string message = null;
                                var deliveryTime = string.Format(CultureInfo.GetCultureInfo("zh-CN"), "{0:U}", carrierOrder.DeliveryTime);
                                if (paymentResultEventData.Status == PaymentsSystem.Dto.PaymentStatusOutPut.PaySuccess)
                                {
                                    findEntity.SucceededTime = paymentResultEventData.PayTime ?? DateTime.Now;
                                    dbContext.SaveChanges();
                                    message = $"{deliveryTime}" +
                                        $"从{carrierOrder.TransportLinkage.StartingPlaceName}到{carrierOrder.TransportLinkage.DestinationPlaceName}" +
                                        $"（{carrierOrder.OrderNumber}）{(carrierOrder.Type == OrderType.Bulk ? "" : "[零担]")}" +
                                        $"{findEntity.Type.GetCustomAttribute<EnumTextMetaAttribute>().Text}" +
                                        $"{findEntity.Price}元您转账成功。";
                                }
                                else
                                {
                                    message = $"{deliveryTime}" +
                                        $"从{carrierOrder.TransportLinkage.StartingPlaceName}到{carrierOrder.TransportLinkage.DestinationPlaceName}" +
                                        $"（{carrierOrder.OrderNumber}）{(carrierOrder.Type == OrderType.Bulk ? "" : "[零担]")}" +
                                        $"{findEntity.Type.GetCustomAttribute<EnumTextMetaAttribute>().Text}" +
                                        $"{findEntity.Price}元您转账失败，" +
                                        $"失败原因：{paymentResultEventData?.ErrorInfo ?? "未知"}。";
                                }
                                var orderModel = this.ToAppOrderModel(carrierOrder, findEntity.UserId, dbContext);
                                await this.SendRongCloudMessage("System", findEntity.UserId, message, orderModel.ToJson());
                            }
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                this._logger.LogError(ex.Message + Environment.NewLine + ex.StackTrace, ex);
            }
        }

        private async Task ProcessOrderStartMessageAsync(List<string> orderIds)
        {
            try
            {
                using (var serviceScope = this._serviceProvider.GetRequiredService<IServiceScopeFactory>().CreateScope())
                {
                    using (var dbContext = serviceScope.ServiceProvider.GetService<ApplicationDbContext>())
                    {
                        var quotations = dbContext.Quotations
                            .AsNoTracking()
                            .Include(x => x.User)
                            .ThenInclude(x => x.Car)
                            .Include(x => x.Order)
                            .ThenInclude(x => x.TransportLinkage)
                            .Where(x => orderIds.Contains(x.OrderId)).ToList();
                        if (quotations?.Count() > 0)
                        {
                            orderIds = quotations.Select(z => z.OrderId).ToList();
                            var filteredOrders = await this._tmsServiceBus.GetGrabbedOrdersAsync(orderIds);
                            var filteredOrdersList = filteredOrders?.Output?.List;
                            if (filteredOrdersList?.Count() > 0)
                            {
                                var realTimeOrderInfo = filteredOrdersList.First();
                                if (!string.IsNullOrWhiteSpace(realTimeOrderInfo.OrderNumber))
                                {
                                    var carrierOrder = AutoMapper.Mapper.Map(realTimeOrderInfo, new ConfirmedOrder());
                                    var deliveryTime = string.Format(CultureInfo.GetCultureInfo("zh-CN"), "{0:U}", realTimeOrderInfo.DeliveryTime);
                                    var startPlace = this._areaService.ShortenAreaName(carrierOrder.TransportLinkage.StartingPlaceName);
                                    var destinationPlace = this._areaService.ShortenAreaName(carrierOrder.TransportLinkage.DestinationPlaceName);
                                    foreach (var quotation in quotations)
                                    {
                                        string message = null;
                                        var isWin = false;
                                        if (string.Equals(realTimeOrderInfo.CarCode, quotation.User.Car.Code, StringComparison.InvariantCultureIgnoreCase))
                                        {
                                            isWin = true;
                                            message = $"{deliveryTime} " +
                                                $"从{startPlace}到{destinationPlace}" +
                                                $"（{quotation.Order.OrderNumber}）您抢单成功！请按时前往指定地点装货！谢谢！";
                                            this._eventBus.Post(new PushNotificationInputMessage()
                                            {
                                                AliaType = AliaTypes.PhoneNumber,
                                                Alias = new string[] { quotation.User.PhoneNumber },
                                                Arguments = new Dictionary<string, string>()
                                                {
                                                    { "Handler", "Toast" },
                                                    { "Message", message },
                                                },
                                                Badge = 0,
                                                Text = message,
                                                Ticker = $"车牌号为{realTimeOrderInfo.CarCode}的用户抢单成功",
                                                Title = "抢单成功",
                                                Platform = PlatformId.CCP,
                                                Verb = PushNotificationVerbs.BroadCast
                                            }, TimeSpan.Zero);
                                        }
                                        else
                                        {
                                            message = $"{deliveryTime} " +
                                                $"从{startPlace}到{destinationPlace}" +
                                                $"（{quotation.Order.OrderNumber}）您未抢单成功！感谢您的报价。祝您下次抢单成功。";
                                            this._eventBus.Post(new PushNotificationInputMessage()
                                            {
                                                AliaType = AliaTypes.PhoneNumber,
                                                Alias = new string[] { quotation.User.PhoneNumber },
                                                Arguments = new Dictionary<string, string>()
                                                {
                                                    { "Handler", "Toast" },
                                                    { "Message", message },
                                                },
                                                Badge = 0,
                                                Text = message,
                                                Ticker = $"车牌号为{realTimeOrderInfo.CarCode}的用户抢单成功",
                                                Title = "抢单失败",
                                                Platform = PlatformId.CCP,
                                                Verb = PushNotificationVerbs.BroadCast
                                            }, TimeSpan.Zero);
                                        }
                                        var orderModel = this.ToAppOrderModel(quotation, dbContext, isWin);
                                        await this.SendRongCloudMessage("System", quotation.User.Id, message, orderModel.ToJson());
                                    }
                                }
                            }
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                this._logger.LogError(ex.Message + Environment.NewLine + ex.StackTrace, ex);
            }
        }

        private async Task SendRongCloudMessage(string fromUserId, string toUserId, string message, string extra = "")
        {
            try
            {
                var cfg = this.Configuration.GetSection("Data:DefaultConnection:RongIoConnection");
                var contentEntity = new { content = message, extra = extra };
                var content = contentEntity.ToJson();
                var userIds = new string[] { toUserId };
                var obj = new
                {
                    appKey = cfg["AppKey"],
                    appSecret = cfg["AppSecret"],
                    fromUserId = fromUserId,
                    toUserIds = userIds,
                    objectName = "RC:TxtMsg",
                    content = content
                };
                var bodyContent = JsonConvert.SerializeObject(obj);
                var body = new StringContent(bodyContent, System.Text.Encoding.UTF8, "application/json");
                using (var client = new HttpClient())
                {
                    client.BaseAddress = new Uri("http://ccprongyun.chinacloudsites.cn/api/");
                    this._logger.LogError($"Sending to RongCloudService: \r\n{bodyContent}.");
                    var response = await client.PostAsync($"RongCloud/ChatHistory?msgType=true", body);
                    if (!response.IsSuccessStatusCode)
                    {
                        this._logger.LogError($"Failed to send RongCloud message to {toUserId}.");
                    }
                    this._logger.LogError($"RongCloudService returned: \r\n{await response.Content.ReadAsStringAsync()}.");
                }
            }
            catch (Exception ex)
            {
                this._logger.LogError(ex.Message + Environment.NewLine + ex.StackTrace);
            }
        }

        private Sino.CapacityCloud.WebApi.Order ToAppOrderModel(Repositories.Models.Quotation quotation, ApplicationDbContext dbContext, bool isWin = true)
        {
            var lines = dbContext.FavouriteTransportLinkages
                .AsNoTracking()
                .Include(x => x.TransportLinkage)
                .Where(x => x.UserId == quotation.UserId).ToList();
            var r = AutoMapper.Mapper.Map(quotation.Order, new WebApi.Order());
            r.IsFocus = lines.Any(z => quotation.Order.TransportLinkage.IsCompatiable(z.TransportLinkage));
            r.GrabComment = quotation?.Remarks ?? "";
            r.Price = SystemUtils.Try(() => $"{quotation.Price} {quotation.Unit.GetCustomAttribute<EnumTextMetaAttribute>().Text}", "");
            r.OrderGrabbedState = isWin ? OrderGrabbedState.Win : OrderGrabbedState.Lose;
            r.OrderSuppliedState = quotation != null ? OrderSuppliedState.Grabbed : OrderSuppliedState.Ungrabbed;
            return r;
        }

        private Sino.CapacityCloud.WebApi.Order ToAppOrderModel(Repositories.Models.ConfirmedOrder order, string userId, ApplicationDbContext dbContext, bool isWin = true)
        {
            var lines = dbContext.FavouriteTransportLinkages
                .AsNoTracking()
                .Include(x => x.TransportLinkage)
                .Where(x => x.UserId == userId).ToList();
            var quotations = dbContext.Quotations.AsNoTracking().Where(z => z.UserId == userId).ToList();
            var quotation = quotations.FirstOrDefault(z => z.OrderId == order.ConfirmedOrderId);
            var r = AutoMapper.Mapper.Map(order, new WebApi.Order());
            r.IsFocus = lines.Any(z => order.TransportLinkage.IsCompatiable(z.TransportLinkage));
            r.GrabComment = quotation?.Remarks ?? "";
            r.Price = SystemUtils.Try(() => $"{quotation.Price} {quotation.Unit.GetCustomAttribute<EnumTextMetaAttribute>().Text}", "");
            r.OrderGrabbedState = isWin ? OrderGrabbedState.Win : OrderGrabbedState.Lose;
            r.OrderSuppliedState = OrderSuppliedState.Grabbed;
            return r;
        }

    }

}